package org.jeecg.common.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.jeecg.common.core.Result;
import org.jeecg.common.rc.aop.dicttrans.dictaop.entity.Describetor;
import org.jeecg.common.util.util_entity.Bean2DMLEnum;
import org.jeecg.common.util.util_entity.BeanDoc;
import org.jeecg.common.util.util_entity.DiffDoc;
import org.jeecg.common.util.util_entity.Info;
import org.jeecg.common.util.util_entity.core.BaseEntity;
import com.github.houbb.data.factory.core.util.DataUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.format.annotation.DateTimeFormat;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodType;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.hutool.core.util.StrUtil.isBlank;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

/**
 * 反射工具类
 *
 * @author zjarlin
 * @since 2022/06/29
 */
@SuppressWarnings("unused")

public interface RefUtil {


    String FIELD_TYPE_COLL = "Collection<T>";
    String FIELD_TYPE_T = "T";

    static String mock(Class<?> clazz) {
        Object build = DataUtil.build(clazz);
        return JSON.toJSONString(build);
    }

    /**
     * 打印diff bean文档
     *
     * @param beanA 豆doca
     * @param beanB 豆docb
     * @author zjarlin
     * @since 2022/06/13
     */

    static <A, B> void printDiffBean(Class<A> beanA, Class<B> beanB) {
        final List<BeanDoc> beanDoc = getBeanDoc(beanA);
        final List<BeanDoc> beanDoc1 = getBeanDoc(beanB);
        System.out.println("括号里形参A的属性个数X:" + beanDoc.size());
        System.out.println("括号里形参B的属性个数Y:" + beanDoc1.size());
        System.out.println(JSON.toJSON(diffBean(beanA, beanB)));
    }

    /**
     * 得到JavaBeanDocList(字段名,字段类型,字段注释)
     *
     * @param clazz clazz
     * @return {@link List }<{@link BeanDoc }>
     * @author zjarlin
     * @since 2022/06/13
     */
    @SneakyThrows
    static <T> List<BeanDoc> getBeanDoc(Class<T> clazz) {
        T t = clazz.getDeclaredConstructor().newInstance();
        List<BeanDoc> beanDoc1 = getBeanDoc(t);
        return beanDoc1;
    }

    /**
     * 得到JavaBeanDocList(字段名,字段类型,字段注释)
     *
     * @param t t 入参
     * @return {@link List }<{@link BeanDoc }>
     * @author zjarlin
     * @since 2023/05/25
     */

    /**
     * 得到JavaBeanDocList(字段名,字段类型,字段注释)
     *
     * @param t t 入参
     * @return {@link List }<{@link BeanDoc }>
     * @author zjarlin
     * @since 2023/05/25
     */
    static <T, A extends Annotation, AS extends Annotation> List<BeanDoc> getBeanDoc(T t) {
        return getBeanDoc(t, true, false);
    }

    /**
     * 得到JavaBeanDocList(字段名,字段类型,字段注释)
     *
     * @param ignoreNull 忽略控制
     * @param recursion  默认不递归
     * @return {@link List }<{@link BeanDoc }>
     * @author zjarlin
     * @since 2023/05/25
     */
    static <T, A extends Annotation, AS extends Annotation> List<BeanDoc> getBeanDoc(T t, boolean ignoreNull, boolean recursion) {
        Class<?> aClass = t.getClass();
        final Field[] fields = ReflectUtil.getFields(aClass);
        int[] a = {0};
        List<BeanDoc> ret = new LinkedList<>();

        List<BeanDoc> collect1 = Arrays.stream(fields)
                .peek(e -> e.setAccessible(true))


                //过滤静态字段
                .filter(field -> !Modifier.isStatic(field.getModifiers()))

                // 这里我要在boolean ignoreNull为true时过滤掉空值属性
                .filter(field -> {
                    Object fieldValue = ReflectUtil.getFieldValue(t, field.getName());
                    boolean aNull = Objects.isNull(fieldValue);
                    return !(ignoreNull && aNull);
                })
                // 空集合过滤
                .filter(field -> {
                    Object fieldValue = ReflectUtil.getFieldValue(t, field);
                    boolean isEmptyCollection = Collection.class.isAssignableFrom(field.getType()) && CollUtil.isEmpty((Collection<?>) fieldValue);
                    return !(ignoreNull && isEmptyCollection);
                })
                .map(field -> {
                    BeanDoc beanDoc = new BeanDoc();
                    beanDoc.setRootObject(t);
                    beanDoc.setRootObjectClass(aClass);
                    beanDoc.setField(field);
                    String fieldName = field.getName();
                    beanDoc.setFieldName(fieldName);
                    beanDoc.setFieldType(field.getType());
                    beanDoc.setSimpleTypeName(field.getType().getSimpleName());
                    beanDoc.setAnnotations(field.getAnnotations());
                    beanDoc.setComment(ofNullable(field.getAnnotation(ApiModelProperty.class)).map(ApiModelProperty::value).orElse("没有ApiModelProperty修饰" + a[0]++));
                    beanDoc.setRunTimeValue(ReflectUtil.getFieldValue(t, field));
                    // 处理嵌套对象属性
                    Object nestedObject = ReflectUtil.getFieldValue(t, field);
                    if (isObjectField(t, field))
                        beanDoc.setFieldEnum(FIELD_TYPE_T);
                    if (isObjectField(t, field) && recursion) {
                        List<BeanDoc> nestedBeanDocs = getBeanDoc(nestedObject, ignoreNull, false);
                        nestedBeanDocs.forEach(e -> {
//                            Object rootObject = e.getRootObject();
                            e.setSuperObjectFieldTypeEnum(FIELD_TYPE_T);
                            e.setSuperObjectFieldName(fieldName);
                            e.setSuperObject(t);
//                            e.setRootObject(rootObject);
                        });
                        ret.addAll(nestedBeanDocs);
                    }

                    // 处理集合属性
                    if (isCollectionField(field))
                        beanDoc.setFieldEnum(FIELD_TYPE_COLL);
                    if (isCollectionField(field) && recursion) {
                        Collection<?> collection = (Collection<?>) nestedObject;
                        if (!ignoreNull && CollUtil.isNotEmpty(collection)) {
//                            Object next = collection.iterator().next();
                            Set<BeanDoc> nestedBeanDocs = collection.stream().flatMap(e -> getBeanDoc(e, false, false).stream()).collect(Collectors.toSet());
//                            List<BeanDoc> nestedBeanDocs = getBeanDoc(next, ignoreNull, false);
                            nestedBeanDocs.forEach(e -> {
                                e.setSuperObject(t);
                                e.setSuperObjectFieldTypeEnum(FIELD_TYPE_COLL);
                                e.setSuperObjectFieldName(fieldName);
                            });
                            ret.addAll(nestedBeanDocs);
                        }
                    }
                    return beanDoc;
                }).collect(Collectors.toList());
        ret.addAll(collect1);
        return ret;
    }

    /**
     * diff bean文档
     *
     * @param beanA beana
     * @param beanB beanb
     * @return {@link List }<{@link DiffDoc }>
     * @author zjarlin
     * @since 2022/06/13
     */
    static <A, B> List<DiffDoc> diffBean(Class<A> beanA, Class<B> beanB) {

        List<BeanDoc> beanDoc = getBeanDoc(beanA);

        List<BeanDoc> beanDoc1 = getBeanDoc(beanB);
//
        if (beanDoc.size() < beanDoc1.size()) {
            System.out.println("由于beanA的属性少于beanB，所以自动翻转形参,打印的json里的A是括号里形参字段个数多的那个");
        }
        final List<BeanDoc> beanDocA = beanDoc.size() < beanDoc1.size() ? beanDoc1 : beanDoc;
        final List<BeanDoc> beanDocB = beanDoc.size() < beanDoc1.size() ? beanDoc : beanDoc1;
//
        final Map<String, BeanDoc> relationFromB = beanDocB.stream().collect(Collectors.toMap(BeanDoc::getFieldName, Function.identity()));
        final Map<String, BeanDoc> relationCommentFromB = beanDocB.stream().collect(Collectors.toMap(BeanDoc::getComment, Function.identity()));
        return beanDocA.stream().map(a -> {
            final String fieldNameA = a.getFieldName();
            final String simpleTypeNameA = a.getSimpleTypeName();
            final String commentA = ofNullable(a.getComment()).orElse("");

            //名字精确找
            final String fieldNameB = ofNullable(relationFromB.get(fieldNameA)).map(BeanDoc::getFieldName).orElse(
                    beanDocB.stream().filter(bs -> {
                        final String fieldName = bs.getFieldName();
//                      名字正反向模糊找 :考虑反向包含的情况(可能子串包含但语义不同,会有bug)   比如A里  abcQWabcEasdas   B里bc||ab||QW 也能like出来
                        return containsIgnoreOrder(fieldName, fieldNameA);
                    }).map(BeanDoc::getFieldName).findAny().orElse(
                            // 注释精确找
                            ofNullable(relationCommentFromB.get(commentA)).map(BeanDoc::getFieldName).orElse(
                                    beanDocB.stream().filter(bs -> {
//                                        注释模糊找
                                        final String bsComment = ofNullable(bs.getComment()).orElse("");
                                        return containsIgnoreOrder(bsComment, commentA);
                                    }).map(BeanDoc::getFieldName).findAny().orElse("")
                            )));
            final String simpleTypeNameB = ofNullable(relationFromB.get(fieldNameA)).map(BeanDoc::getSimpleTypeName).orElse(
                    beanDocB.stream().filter(bs -> {
                        final String simpleTypeName = bs.getSimpleTypeName();
                        return containsIgnoreOrder(simpleTypeName, simpleTypeNameA);
                    }).map(BeanDoc::getSimpleTypeName).findAny().orElse("")
            );

            final String commentB = ofNullable(relationFromB.get(fieldNameA)).filter(e -> Objects.nonNull(e.getComment())).map(BeanDoc::getComment).orElse(
                    beanDocB.stream().filter(bs -> {
                        final String comment = ofNullable(bs.getComment()).orElse("");
                        return containsIgnoreOrder(comment, commentA);
                    }).map(BeanDoc::getComment).findAny().orElse(
                            ofNullable(relationCommentFromB.get(commentA)).map(BeanDoc::getComment).orElse(
                                    beanDocB.stream().filter(bs -> {
//                                        注释模糊找
                                        final String bsComment = ofNullable(bs.getComment()).orElse("");
                                        return containsIgnoreOrder(bsComment, commentA);
                                    }).map(BeanDoc::getComment).findAny().orElse("")

                            )
                    )
            );
            return new DiffDoc(fieldNameA, simpleTypeNameA, commentA, fieldNameB, simpleTypeNameB, commentB);
        }).collect(Collectors.toList());
    }

    public static Map<String, String> getBeanDes(Class<?> cls) {
        Map<String, String> fieldDesMap = new LinkedHashMap<>();
        processFields(cls, fieldDesMap);
        return fieldDesMap;
    }

    public static void processFields(Class<?> cls, Map<String, String> fieldDesMap) {
        Class<BaseEntity> baseEntityClass = BaseEntity.class;

        List<String> collect2 = Arrays.stream(baseEntityClass.getDeclaredFields()).map(e -> {
            return e.getName();
        }).collect(Collectors.toList());
        collect2.add("id");
        collect2.add("sysOrgCode");
        collect2.add("url");
        collect2.add("qrCode");
        collect2.add("fileName");
        String[] array = collect2.stream().toArray(String[]::new);

        Class<?> currentClass = cls;
        while (currentClass != null) {
            Field[] fields = currentClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(ApiModelProperty.class) && !Modifier.isStatic(field.getModifiers())) {
                    String name = field.getName();
                    String value = field.getAnnotation(ApiModelProperty.class).value();
                    if (!StrUtil.containsAnyIgnoreCase(value, array)) {
                        if (List.class.isAssignableFrom(field.getType())) {
                            // For List<T> field, process the generic type T
                            ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                            Class<?> genericClass = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                            String listFieldDes = getBeanDes(genericClass).toString(); // You can modify this to return a more meaningful representation
                            fieldDesMap.put(name, listFieldDes);
                        } else {
                            fieldDesMap.put(name, value);
                        }
                    }
                }
            }
            currentClass = currentClass.getSuperclass();
        }
    }

    public static Map<String, String> getBeanDes2(Class<?> cls) {
        Class<BaseEntity> baseEntityClass = BaseEntity.class;
        List<String> collect2 = Arrays.stream(baseEntityClass.getDeclaredFields()).map(e -> {
            return e.getName();
        }).collect(Collectors.toList());
        collect2.add("id");
        collect2.add("sysOrgCode");
        collect2.add("url");
        collect2.add("qrCode");
        collect2.add("fileName");
        String[] array = collect2.stream().toArray(String[]::new);
        Map<String, String> collect = Arrays.stream(cls.getDeclaredFields())
                .filter(field -> {
                    boolean annotationPresent = field.isAnnotationPresent(ApiModelProperty.class);
                    return annotationPresent;
                })
                //过滤静态字段
                .filter(field -> !Modifier.isStatic(field.getModifiers()))
                .filter(field -> {
                    String name = field.getName();
                    boolean b = StrUtil.equalsAnyIgnoreCase(name, array);
                    String value = field.getAnnotation(ApiModelProperty.class).value();
                    boolean b1 = StrUtil.containsAnyIgnoreCase(value, "主键", "外键", "id");
                    return
                            !b
                            &&
                            !b1
                            ;
                })
                .collect(Collectors.toMap(e -> e.getName(), e -> {
                    ApiModelProperty annotation = e.getAnnotation(ApiModelProperty.class);
                    String des = ofNullable(annotation.value()).filter(StrUtil::isNotBlank).orElse("");
                    return des;
                }));
        return collect;
    }

    /**
     * 包含忽略秩序
     *
     * @param seq       seq
     * @param searchSeq 搜索seq
     * @return 返回状态true/false
     * @author zjarlin
     * @since 2022/06/18
     */
    static boolean containsIgnoreOrder(final CharSequence seq, final CharSequence searchSeq) {
        return StringUtils.contains(seq, searchSeq) || StringUtils.contains(searchSeq, seq);
    }

//    static void main(String[] args) {
//        final MethodHandles.Lookup lookup = MethodHandles.lookup();
//        final SFunction<Integer, String> lineHandler = String::valueOf;
//        final Cloneable generic4SerializedFunction1 = getGeneric4SerializedFunction(lineHandler);
//        System.out.println(generic4SerializedFunction1);
//    }


    /**
     * 利用SerializedLambda获取Function的入参出参泛型
     * 有bug
     *
     * @param fun 序列化的function
     * @return 返回信息
     * @author zjarlin
     * @since 2022/07/27
     */
    @SneakyThrows
    static <T, R, F extends Function<T, R> & Serializable> Cloneable getGeneric4SerializedFunction(F fun) {
        final Class<? extends Function> aClass = fun.getClass();
        final ClassLoader classLoader = aClass.getClassLoader();
        Method writeReplace = aClass.getDeclaredMethod("writeReplace");
        writeReplace.setAccessible(true);
        SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(fun);
        final String instantiatedMethodType = serializedLambda.getInstantiatedMethodType();
        final MethodType methodType = MethodType.fromMethodDescriptorString(instantiatedMethodType, classLoader);
        final Class<?> parameterType = methodType.parameterType(0);
        final Class<?> returnType = methodType.returnType();
        return new HashMap<Class<?>, Class<?>>(1) {{
            put(parameterType, returnType);
        }};
    }

    /**
     * 反射copy集合 List<A> -> List<B>  字段名,类型相同就copy
     *
     * @param resources
     * @return {@link List }<{@link T }>
     * @author addzero
     * @since 2022/09/21
     */
    @SneakyThrows
    static <T> List<T> copyCollection(List<T> resources) {
        return copyCollection(resources, (Class<T>) resources.get(0).getClass());
    }

    //public static <T> void dasDash(Collection<T> src,Collection<T> tar) {
    //
    //    List<T> studDao = new ArrayList<T>();
    //    List<student> list = studentService.list();
    //    list.forEach(stu ->{
    //        TuserVO = new T();
    //        BeanUtils.copyProperties(stu, userVO);
    //        studDao.add(userVO);
    //    });
    //}

    /**
     * 反射copy集合 List<A> -> List<B>  字段名,类型相同就copy
     *
     * @param resources
     * @param clz       入参
     * @return {@link List }<{@link T }>
     * @author addzero
     * @since 2022/09/21
     */
    @SneakyThrows
    static <T> List<T> copyCollection(List<?> resources, Class<T> clz) {
        if (CollUtil.isEmpty(resources)) {
            return Collections.emptyList();
        }
        List<T> result = new ArrayList<>();
        T single;
        // 获取源集合泛型的class
        Class<?> sourceClass = resources.get(0).getClass();
        List<Field> sourceFields = new ArrayList<>();
        // 递归获取自己和父类的字段
        sourceFields = getSuperField(sourceClass, sourceFields);
        // 获取目标类型的字段
        List<Field> targetFields = new ArrayList<>();
        targetFields = getSuperField(clz, targetFields);
        // 转成 key 是字段名， value 是字段类型的 map，方便校验
        Map<String, ? extends Class<?>> fieldNameTypeMap = targetFields.stream().collect(Collectors.toMap(Field::getName, Field::getType));

        PropertyDescriptor propertyDescriptor;
        for (Object resource : resources) {
            // 实例化目标类型
            single = clz.newInstance();
            for (Field declaredField : sourceFields) {
                declaredField.setAccessible(true);
                String fieldName = declaredField.getName();
                // 字段不被 finla 修饰，并且目标类包含此字段，并且字段类型一致才能继续走逻辑
                if (Modifier.isFinal(declaredField.getModifiers())
                    || !fieldNameTypeMap.containsKey(fieldName)
                    || !fieldNameTypeMap.get(fieldName).equals(declaredField.getType())) {
                    continue;
                }
                // 获取字段值
                Object value = declaredField.get(resource);
                // 获取字段的 set 方法，并设置值
                propertyDescriptor = new PropertyDescriptor(fieldName, clz);
                propertyDescriptor.getWriteMethod().invoke(single, value);
            }
            result.add(single);
        }

        return result;
    }

    /**
     * 递归获取自己和父类的全部字段,包括父类的父类
     *
     * @param clz
     * @param result
     * @return
     */
    static List<Field> getSuperField(Class<?> clz, List<Field> result) {
        Class<?> superclass = clz.getSuperclass();
        result.addAll(Arrays.asList(clz.getDeclaredFields()));
        if (ObjectUtils.isEmpty(superclass)) {
            return result;
        }
        return getSuperField(superclass, result);

    }

    /**
     * 得到实际类型
     *
     * @param t t 入参
     * @return 返回实际类型
     * @author zjarlin
     * @since 2022/07/26
     */
    static <T> Type getActualType(T t) {
        final Type genericSuperclass = t.getClass().getGenericSuperclass();
        return ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
    }

    /**
     * 判断下层接口返回的结果是否是new出来的对象
     *
     * @param object 下层接口返回的obj
     * @return 返回状态true/false
     * @author zjarlin
     * @since 2022/06/11
     */
    static boolean isNew(Object object) {
        Class<?> aClass = object.getClass();
        Field[] fields = aClass.getDeclaredFields();
        return Arrays.stream(fields)
                .filter(field -> !Modifier.isStatic(field.getModifiers()))
                .map(field -> {
                    field.setAccessible(true);
                    try {
                        return field.get(object);
                    } catch (IllegalAccessException e) {
                        return null;
                    }
                })
                .allMatch(value -> value == null ||
                                   (value instanceof String && StringUtils.isBlank((String) value)) ||
                                   (value instanceof Collection && CollUtil.isEmpty((Collection<?>) value)));
    }

    /**
     * 填充字段
     *
     * @param t      t
     * @param setFun set方法
     * @param getFun get方法
     * @author zjarlin
     * @since 2022/06/29
     */
    static <T, S, F> void fillField(T t, BiConsumer<T, F> setFun, Function<S, F> getFun) {
        fillField(t, new Info<>(setFun, getFun));
    }

    /**
     * 填充字段
     *
     * @param s     s
     * @param infos 信息
     * @param t     t
     * @author zjarlin
     * @since 2022/06/29
     */
    @SafeVarargs
    static <T, S, F> void fillField(T t, S s, Info<T, S, F>... infos) {
        Arrays.stream(infos).filter(info -> Objects.nonNull(info.getFun.apply(s))).forEach(info -> {
            final F apply = info.getFun.apply(s);
            if (isNotBlank(apply instanceof String ? ((String) apply) : null)) {
                info.setFun.accept(t, apply);
                return;
            }
            info.setFun.accept(t, (F) (apply instanceof Integer ? ((Integer) apply) : null));
        });
    }

    /**
     * 填充字段
     *
     * @param s      s
     * @param t      t
     * @param setFun set方法
     * @param getFun get方法
     * @author zjarlin
     * @since 2022/06/29
     */
    static <T, S, F> void fillField(T t, S s, BiConsumer<T, F> setFun, Function<S, F> getFun) {
        fillField(t, s, new Info<>(setFun, getFun));
    }

    @SneakyThrows
    static void printAddDML() {
        StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2];
        String className = stackTraceElement.getClassName();
        Class<?> aClass = Class.forName(className);
        printAddDML(aClass);
    }

    static void printAddDML(final Class<?> aClass) {
        List<String> dmls = getAddDMLs(aClass);
        //String sqlDML = (String) comment;
        dmls.forEach(System.out::println);

        //System.out.println(sqlDML);
    }

    @NotNull
    public static List<String> getAddDMLs(Class<?> aClass) {
        String tabName =
                Optional.ofNullable(aClass.getAnnotation(TableName.class))
                        .map(TableName::value)
                        .orElse("");
        Field[] declaredFields = ReflectUtil.getFields(aClass);

        List<String> dmls = Arrays.stream(declaredFields)
                .filter(field -> !field.isAnnotationPresent(TableField.class) || field.getAnnotation(TableField.class).exist())
                //过滤静态字段
                .filter(field -> !Modifier.isStatic(field.getModifiers()))
                .map(field -> {
                    String fieldName = field.getName();

                    String colComment = ofNullable(field.getAnnotation(ApiModelProperty.class))
                            .map(ApiModelProperty::value)
                            .orElse("");

                    String col = StrUtil.toUnderlineCase(fieldName);
                    Class<?> type = field.getType();
                    String colType = Bean2DMLEnum.get(type, Bean2DMLEnum::getSqlType);
                    String colLength = Bean2DMLEnum.get(type, Bean2DMLEnum::getLength);
                    //修复url变量的字段长度不够问题
                    if (StrUtil.containsAnyIgnoreCase(fieldName, "url","base64","text") && String.class.isAssignableFrom(type)) {
                        colType = "longtext";
                        colLength = "";
                    }

                    if (type == Date.class) {
                        DateTimeFormat declaredAnnotation = field.getDeclaredAnnotation(DateTimeFormat.class);
                        String pattern = declaredAnnotation.pattern();
                        boolean equals = StrUtil.equals(pattern, "yyyy-MM-dd HH:mm:ss");
                        colType = equals ? "datetime" : Bean2DMLEnum.get(type, Bean2DMLEnum::getSqlType);
                    }


                    String dml = StrUtil.concat(false, "alter table `"
                            , tabName
                            , "` add "
                            , "`", col, "` ", colType, colLength, " null ", "comment ", "'", colComment, "'");
                    return dml + ";";
                }).collect(Collectors.toList());
        return dmls;
    }

    static void printChangeDML(final Class<?> aClass
            , String oldColName
            , String newColName
            , Bean2DMLEnum newColType, String newColComment) {
        String tabName =
                Optional.ofNullable(aClass.getAnnotation(TableName.class))
                        .map(TableName::value)
                        .orElse("");
        Field[] declaredFields = aClass.getDeclaredFields();

        String sqlDML = Arrays.stream(declaredFields)
                .filter(e -> StrUtil.equals(e.getName(), oldColName))
                .map(field -> {

                    String name = field.getName();

                    String colComment =
                            isBlank(newColComment) ?
                                    Optional.ofNullable(field.getAnnotation(ApiModelProperty.class))
                                            .map(ApiModelProperty::value)
                                            .orElse("") : newColComment;

                    String col = StrUtil.toUnderlineCase(name);

                    String newCol = isBlank(newColName) ? col : StrUtil.toUnderlineCase(newColName);
                    Class<?> type = field.getType();

                    Class<?> newType =
                            Optional.ofNullable(newColType)
                                    .map(e -> e.javaType)
                                    .orElse(null);
                    String colType = newColType == null
                            ? Bean2DMLEnum.get(type, Bean2DMLEnum::getSqlType)
                            : Bean2DMLEnum.get(newType, Bean2DMLEnum::getSqlType);

                    String colLength =
                            newColType == null
                                    ? Bean2DMLEnum.get(type, Bean2DMLEnum::getLength)
                                    : Bean2DMLEnum.get(newType, Bean2DMLEnum::getLength);

                    String dml = StrUtil.concat(false, "alter table `"
                            , tabName
                            , "` change "
                            , "`", col, "` "
                            , "`", newCol, "` "
                            , colType, colLength, " null ", "comment ", "'", colComment, "'");
                    return dml;
                }).collect(Collectors.joining(";" + System.lineSeparator()));
        System.out.println(sqlDML);
    }


    static <AS extends Annotation, A extends Annotation> List<Describetor<A>> findAnnoMap(Object obj, Class<A> annotationClass, Class<AS> annotationClass1, Function<AS, A[]> fun) {
        List<Describetor<A>> annoMap = findAnnoMap(obj, annotationClass, annotationClass1, fun, true, false);
        return annoMap;
    }

    public static List<String> searchTName(Object obj) {
        List<BeanDoc> beanDoc = getBeanDoc(obj);
        List<String> collect = beanDoc.stream().filter(e -> {
            String fieldEnum = e.getFieldEnum();
            boolean equals = Objects.equals(fieldEnum, RefUtil.FIELD_TYPE_T);
            return equals;
        }).map(BeanDoc::getFieldName).collect(Collectors.toList());
        return collect;
    }

    public static List<String> searchCollName(Object obj) {
        List<BeanDoc> beanDoc = getBeanDoc(obj);
        List<String> collect = beanDoc.stream().filter(e -> {
            String fieldEnum = e.getFieldEnum();
            boolean equals = Objects.equals(fieldEnum, RefUtil.FIELD_TYPE_COLL);
            return equals;
        }).map(e -> e.getFieldName()).collect(Collectors.toList());
        return collect;
    }


    static <AS extends Annotation, A extends Annotation> List<Describetor<A>> findAnnoMap(Object obj, Class<A> annotationClass, Class<AS> annotationClass1, Function<AS, A[]> fun, boolean ignoreNull, boolean recursion) {
//        Class<?> aClass = obj.getClass();
//        Field[] declaredFields = ReflectUtil.getFieldsDirectly(aClass, true);
        List<BeanDoc> beanDoc = getBeanDoc(obj, ignoreNull, recursion);

        List<Describetor<A>> collect = beanDoc.stream()
                .filter(e -> {
                    Field field = e.getField();
                    boolean aStatic = Modifier.isStatic(field.getModifiers());
                    return !aStatic;
                })
//                .filter(e -> {
//                    Field field = e.getField();
//                    boolean b = !field.getType().isAssignableFrom(Collection.class);
//                    return b;
//                })
                .filter(e -> {
                    Field field = e.getField();
                    boolean annotationPresent = field.isAnnotationPresent(annotationClass);
                    boolean annotationPresent1 = field.isAnnotationPresent(annotationClass1);
                    return (annotationPresent || annotationPresent1);
                })
                .map(e -> {
//                    BeanDoc e1 = e;
                    Object rootObject = e.getRootObject();
                    Field field = e.getField();
                    String name = field.getName();
                    Class<?> type = field.getType();
                    Annotation[] annotations = field.getAnnotations();
//                    Object fieldValue = ReflectUtil.getFieldValue(rootObject, field);

                    Describetor<A> describetor = new Describetor<>();
                    BeanUtil.copyProperties(e, describetor);
                    //要搜索的注解
                    describetor.setNeedSearchAnnotation(field.getAnnotation(annotationClass));
                    describetor.setFieldValue(e.getRunTimeValue());
                    //可重复注解搜索
                    A declaredAnnotation1 = field.getDeclaredAnnotation(annotationClass);
                    Optional<A> declaredAnnotation = ofNullable(declaredAnnotation1);
                    Optional<A[]> dictCode2Names = ofNullable(field.getDeclaredAnnotation(annotationClass1)).map(fun);
                    List<A> as = declaredAnnotation.map(Arrays::asList).orElseGet(() -> dictCode2Names.map(Arrays::stream).orElseGet(Stream::empty).collect(Collectors.toList()));
                    describetor.setNeedSearchAnnotations((as));
                    return describetor;
                })
                .collect(Collectors.toList());
        return collect;
    }


    static boolean isT(Object obj) {
        if (Objects.isNull(obj)) {
            return false;
        }
        //非原始类型
        boolean primitive = obj.getClass().isPrimitive();

        try {
            JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(obj));
        } catch (Exception e) {
            return false;
        }
        return
                !isPrimitiveOrWrapper(obj.getClass())
                && !IPage.class.isAssignableFrom(obj.getClass())
                && !Collection.class.isAssignableFrom(obj.getClass())
                && !BigDecimals.class.isAssignableFrom(obj.getClass())
                && !Enum.class.isAssignableFrom(obj.getClass())
                && !JSON.class.isAssignableFrom(obj.getClass())
                && !Result.class.isAssignableFrom(obj.getClass())
                ;
    }

    public static boolean isPrimitiveOrWrapper(Class<?> aClass) {
//        Class<?> aClass = obj.getClass();
        boolean primitive = aClass.isPrimitive();
        return primitive
               || Byte.class.isAssignableFrom(aClass)
               || Short.class.isAssignableFrom(aClass)
               || Integer.class.isAssignableFrom(aClass)
               || Long.class.isAssignableFrom(aClass)
               || Float.class.isAssignableFrom(aClass)
               || Double.class.isAssignableFrom(aClass)
               || Boolean.class.isAssignableFrom(aClass)
               || Character.class.isAssignableFrom(aClass)
                ;
    }

    public static void main(String[] args) {
        boolean primitiveOrWrapper = isPrimitiveOrWrapper(Enum.class);
        System.out.println(primitiveOrWrapper);
    }


    static boolean isCollection(Object obj) {
        boolean assignableFrom = Collection.class.isAssignableFrom(obj.getClass());
        return assignableFrom;
    }

    static boolean isNonNullField(Object obj, Field field) {
        Object fieldValue = ReflectUtil.getFieldValue(obj, field);
        return Objects.nonNull(fieldValue);
    }

    static boolean isObjectField(Object obj, Field field) {
        Object fieldValue = ReflectUtil.getFieldValue(obj, field);
        boolean object = isT(fieldValue);
        return object;
    }


    static boolean isCollectionField(Field field) {
        Class<?> type = field.getType();
        boolean assignableFrom = Collection.class.isAssignableFrom(type);
        return assignableFrom;
    }
}
